<?php

/**
 * Wrapper class around the Twitter API for PHP
 * Based on the class originally developed by David Billingham
 * and accessible at http://twitter.slawcup.com/twitter.class.phps
 * @author David Billingham <david@slawcup.com>
 * @author Aaron Brazell <aaron@technosailor.com>
 * @author Keith Casey <caseysoftware@gmail.com>
 * @version 1.1
 * @package php-twitter
 * @subpackage classes
 */
class twitter {

    /**
     * Authenticating Twitter user
     * @var string
     */
    var $username = 'peppalmer';

    /**
     * Autenticating Twitter user password
     * @var string
     */
    var $password = 'aceituna11';

    /**
     * Recommend setting a user-agent so Twitter knows how to contact you inc case of abuse. Include your email
     * @var string
     */
    var $user_agent = 'pepmanel81@gmail.com';

    /**
     * Can be set to JSON (requires PHP 5.2 or the json pecl module) or XML - json|xml
     * @var string
     */
    var $type = 'json';

    /**
     * It is unclear if Twitter header preferences are standardized, but I would suggest using them.
     * More discussion at http://tinyurl.com/3xtx66
     * @var array
     */
    var $headers = array('Expect:', 'X-Twitter-Client: ', 'X-Twitter-Client-Version: ', 'X-Twitter-Client-URL: ');

    /**
     * @var array
     */
    var $responseInfo = array();

    /**
     * @var boolean
     */
    var $suppress_response_code = false;

    /**
     * @var boolean
     */
    var $debug = false;

    function twitter() {
        // Nothing
    }

    /*     * **** Statuses ***** */

    /**
     * Send a status update to Twitter.
     * @param string $status total length of the status update must be 140 chars or less.
     * @return string|boolean
     */
    function update($status, $replying_to = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/statuses/update.' . $this->type;
        //$status = $this->shorturl($status);
        $postargs = array('status' => $status);
        if ($replying_to)
            $postargs['in_reply_to_status_id'] = (int) $replying_to;

        return $this->objectify($this->process($request, $postargs));
    }

    /**
     * Get @ replies
     * @param integer Optional. Paging of tweets. Number specifies which page of results
     * @param string $since (HTTP-formatted date) Optional.  Narrows the resulting list of direct messages to just those sent after the specified date. 
     * @param integer $since_id Optional. Returns results posted that have an ID greater than $since_id
     * @return string
     * */
    function getReplies($page = false, $since = false, $since_id = false) {
        if (!in_array($this->type, array('xml', 'json', 'rss', 'atom')))
            return false;

        $args = array();
        if ($page)
            $args['page'] = (int) $page;
        if ($since)
            $args['since'] = (string) $since;
        if ($since_id)
            $args['since_id'] = (int) $since_id;

        $qs = '';
        if (!empty($args))
            $qs = $this->_glue($args);

        $request = 'http://twitter.com/statuses/replies.' . $this->type . $qs;
        return $this->objectify($this->process($request));
    }

    /**
     * Destroy a tweet
     * @param integer $id Required.
     * @return string
     * */
    function deleteStatus($id) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/statuses/destroy/' . (int) $id . '.' . $this->type;
        return $this->objectify($this->process($request, true));
    }

    /**
     * Send an unauthenticated request to Twitter for the public timeline. 
     * Returns the last 20 updates by default
     * @param boolean|integer $sinceid Returns only public statuses with an ID greater of $sinceid
     * @return string
     */
    function publicTimeline($sinceid = false) {
        if (!in_array($this->type, array('xml', 'json', 'rss', 'atom')))
            return false;

        $qs = '';
        if ($sinceid !== false)
            $qs = '?since_id=' . intval($sinceid);
        $request = 'http://twitter.com/statuses/public_timeline.' . $this->type . $qs;

        return $this->objectify($this->process($request));
    }

    /**
     * Send an authenticated request to Twitter for the timeline of authenticating user. 
     * Returns the last 20 updates by default
     * @param boolean|integer $id Specifies the ID or screen name of the user for whom to return the friends_timeline. (set to false if you want to use authenticated user).
     * @param boolean|integer $since Narrows the returned results to just those statuses created after the specified date.
     * @deprecated integer $count. As of July 7 2008, Twitter has requested the limitation of the count keyword. Therefore, we deprecate
     * @return string
     */
    function userTimeline($id = false, $count = 20, $since = false, $since_id = false, $page = false) {
        if (!in_array($this->type, array('xml', 'json', 'rss', 'atom')))
            return false;

        $args = array();
        if ($id)
            $args['id'] = $id;
        if ($count)
            $args['count'] = (int) $count;
        if ($since)
            $args['since'] = (string) $since;
        if ($since_id)
            $args['since_id'] = (int) $since_id;
        if ($page)
            $args['page'] = (int) $page;

        $qs = '';
        if (!empty($args))
            $qs = $this->_glue($args);

        if ($id === false)
            $request = 'http://twitter.com/statuses/user_timeline.' . $this->type . $qs;
        else
            $request = 'http://twitter.com/statuses/user_timeline/' . rawurlencode($id) . '.' . $this->type . $qs;

        return $this->objectify($this->process($request));
    }

    /**
     * Returns a single status, specified by the id parameter below.  The status's author will be returned inline.
     * @param integer $id The id number of the tweet to be returned.
     * @return string
     */
    function showStatus($id) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/statuses/show/' . intval($id) . '.' . $this->type;
        return $this->objectify($this->process($request));
    }

    /**
     * Returns the authenticating user's friends, each with current status inline.  It's also possible to request another user's friends list via the id parameter below.
     * @param integer|string $id Optional. The user ID or name of the Twitter user to query.
     * @param integer $page Optional. 
     * @return string
     */
    function friends($id = false, $page = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $args = array();
        if ($id)
            $args['id'] = $page;
        if ($page)
            $args['page'] = (int) $page;

        $qs = '';
        if (!empty($args))
            $qs = $this->_glue($args);

        $request = ( $id ) ? 'http://twitter.com/statuses/friends/' . $id . '.' . $this->type . $qs : 'http://twitter.com/statuses/friends.' . $this->type . $qs;
        return $this->objectify($this->process($request));
    }

    /**
     * Returns the authenticating user's followers, each with current status inline.
     * @param integer $page Optional.
     * @return string
     */
    function followers($page = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/statuses/followers.' . $this->type;
        if ($page)
            $request .= '?page=' . (int) $page;

        return $this->objectify($this->process($request));
    }

    /*     * **** Favorites ***** */

    /**
     * Retrieves favorited tweets
     * @param integer|string $id Required. The username or ID of the user to be fetched
     * @param integer $page Optional. Tweets are returned in 20 tweet blocks. This int refers to the page/block
     * @return string
     */
    function getFavorites($id, $page = false) {
        if (!in_array($this->type, array('xml', 'json', 'rss', 'atom')))
            return false;

        if ($page != false)
            $qs = '?page=' . $page;

        $request = 'http://twitter.com/favorites.' . $this->type . $qs;
        return $this->objectify($this->process($request));
    }

    /**
     * Favorites a tweet
     * @param integer $id Required. The ID number of a tweet to be added to the authenticated user favorites
     * @return string
     */
    function makeFavorite($id) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/favorites/create/' . $id . '.' . $this->type;
        return $this->objectify($this->process($request));
    }

    /**
     * Unfavorites a tweet
     * @param integer $id Required. The ID number of a tweet to be removed to the authenticated user favorites
     * @return string
     */
    function removeFavorite($id) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/favorites/destroy/' . $id . '.' . $this->type;
        return $this->objectify($this->process($request));
    }

    /*     * **** Friendships ***** */

    /**
     * Checks to see if a friendship already exists
     * @param string|integer $user_a Required. The username or ID of a Twitter user
     * @param string|integer $user_b Required. The username or ID of a Twitter user
     * @return string
     */
    function isFriend($user_a, $user_b) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $qs = '?user_a=' . rawurlencode($user_a) . '&amp;' . rawurlencode($user_b);
        $request = 'http://twitter.com/friendships/exists.' . $this->type . $qs;
        return $this->objectify($this->process($request));
    }

    /**
     * Sends a request to follow a user specified by ID
     * @param integer|string $id The twitter ID or screenname of the user to follow
     * @param boolean $notifications Optional. If true, you will recieve notifications from the users updates
     * @return string
     */
    function followUser($id, $notifications = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/friendships/create/' . (int) $id . '.' . $this->type;
        if ($notifications)
            $request .= '?follow=true';

        return $this->objectify($this->process($request));
    }

    /**
     * Unfollows a user
     * @param integer|string $id the username or ID of a person you want to unfollow
     * @return string
     */
    function leaveUser($id) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/friendships/destroy/' . $id . '.' . $this->type;
        return $this->objectify($this->process($request));
    }

    /*     * **** Blocks ***** */

    /**
     * Blocks a user
     * @param integer|string $id the username or ID of a person you want to block
     * @return string
     */
    function blockUser($id) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/blocks/create/' . $id . '.' . $this->type;
        return $this->objectify($this->process($request));
    }

    /**
     * Unblocks a user
     * @param integer|string $id the username or ID of a person you want to unblock
     * @return string
     */
    function unblockUser() {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/blocks/destroy/' . $id . '.' . $this->type;
        return $this->objectify($this->process($request));
    }

    /*     * **** Social Graph ***** */

    /**
     * Returns a list of IDs of all friends for the specified user
     * @param integer $id Required. User ID to request list of friend IDs for
     * return string
     */
    /* @todo same as friends()? */
    function socialGraphFollowing($id = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/friends/ids';
        if ($id)
            $request .= '/' . (int) $id . '.' . $this->type;

        return $this->objectify($this->process($request));
    }

    /**
     * Returns a list of IDs of all followers for the specified user
     * @param integer $id Required. User ID to request list of friend IDs for
     * return string
     */
    /* @todo same as followers() */
    function socialGraphFollowedBy($id = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/followers/ids';
        if ($id)
            $request .= '/' . (int) $id . '.' . $this->type;

        return $this->objectify($this->process($request));
    }

    /*     * **** Users ***** */

    /**
     * Returns extended information of a given user, specified by ID or screen name as per the required
     * id parameter below.  This information includes design settings, so third party developers can theme
     * their widgets according to a given user's preferences.	 
     * @param integer $id Optional. The user ID.
     * @param string $email Optional. The email address of the user being requested (can use in place of $id)
     * @param integer $user_id Optional. The user ID (can use in place of $id)
     * @param string $screen_name Optional. The screen name of the user being requested (can use in place of $id)
     * @return string
     */
    function showUser($id, $email = false, $user_id = false, $screen_name = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        if ($user_id) :
            $qs = '?user_id=' . (int) $user_id;
        elseif ($screen_name) :
            $qs = '?screen_name=' . (string) $screen_name;
        elseif ($email) :
            $qs = '?email=' . (string) $email;
        else :
            $qs = (int) $id;
        endif;

        $request = 'http://twitter.com/users/show/' . $qs . $this->type;

        return $this->objectify($this->process($request));
    }

    /*     * **** Direct Messages ***** */

    /**
     * Returns a list of the direct messages sent to the authenticating user.	 
     * @param string $since (HTTP-formatted date) Optional.  Narrows the resulting list of direct messages to just those sent after the specified date. 
     * @param int $count DEPRECATED. Remains for Backwards Compat
     * @param integer $since_id
     * @param integer $page
     * @deprecated $count
     * @return string
     */
    function directMessages($since = false, $count = null, $since_id = false, $page = false) {
        if (!in_array($this->type, array('xml', 'json', 'rss', 'atom')))
            return false;

        $qs = '?';
        $qsparams = array();
        if ($since !== false)
            $qsparams['since'] = rawurlencode($since);
        if ($since_id)
            $qsparams['since_id'] = (int) $since_id;
        if ($page)
            $qsparams['page'] = (int) $page;

        $request = 'http://twitter.com/direct_messages.' . $this->type . implode('&', $qsparams);

        return $this->objectify($this->process($request));
    }

    /**
     * Returns a list of the sent direct messages from the authenticating user.	 
     * @param string $since (HTTP-formatted date) Optional.  Narrows the resulting list of direct messages to just those sent after the specified date. 
     * @param integer $since_id
     * @param integer $page
     * @deprecated $count
     * @return string
     */
    function sentDirectMessage($since = false, $since_id = false, $page = false) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $qs = '?';
        $qsparams = array();
        if ($since !== false)
            $qsparams['since'] = rawurlencode($since);
        if ($since_id)
            $qsparams['since_id'] = (int) $since_id;
        if ($page)
            $qsparams['page'] = (int) $page;

        $request = 'http://twitter.com/direct_messages/sent.' . $this->type . implode('&', $qsparams);
        return $this->objectify($this->process($request));
    }

    /**
     * Sends a new direct message to the specified user from the authenticating user.  Requires both the user
     * and text parameters below.	 
     * @param string|integer Required. The ID or screen name of the recipient user.
     * @param string $user The text of your direct message.  Be sure to URL encode as necessary, and keep it under 140 characters.  
     * @return string
     */
    function sendDirectMessage($user, $text) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/direct_messages/new.' . $this->type;
        $postargs = 'user=' . rawurlencode($user) . '&text=' . rawurlencode($text);

        return $this->objectify($this->process($request, $postargs));
    }

    /**
     * Deletes a direct message
     * @param integer $id Required
     * @return string
     */
    function deleteDirectMessage($id) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/direct_messages/destroy/' . (int) $id . '.' . $this->type;
        return $this->objectify($this->process($request));
    }

    /*     * **** Account ***** */

    /**
     * Updates delivery device
     * @param string $device Required. Must be of type 'im', 'sms' or 'none'
     * @return string
     */
    function updateDevice($device) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        if (!in_array($device, array('im', 'sms', 'none')))
            return false;

        $qs = '?device=' . $device;
        $request = 'http://twitter.com/account/update_delivery_device.' . $this->type . $qs;
        return $this->objectify($this->process($request));
    }

    /**
     * @param binary Required. Use your script to pass a binary image (GIF, JPG, PNG <700kb) to update Twitter profile avatar
     * @return string
     */
    function updateAvatar($file) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        // Adding @ ensures the POST will be raw multipart data encoded. This MUST be a file, not a URL. Handle it outside of the class.
        $postdata = array('image' => "@$file");
        $request = 'http://twitter.com/account/update_profile_image.' . $this->type;
        return $this->objectify($this->process($request, $postdata));
    }

    /**
     * @param binary Required. Use your script to pass a binary image (GIF, JPG, PNG <800kb) to update Twitter profile avatar. Images over 2048px wide will be scaled down
     * @return string
     */
    function updateBackground($file) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        // Adding @ ensures the POST will be raw multipart data encoded. This MUST be a file, not a URL. Handle it outside of the class.
        $postdata = array('image' => "@$file");
        $request = 'http://twitter.com/account/update_profile_background_image.' . $this->type;
        return $this->objectify($this->process($request, $postdata));
    }

    /**
     * @param array Requires. Pass an array of all optional members: name, email, url, location, or description. Email address must be valid if passed. Refer to Twitter RESTful API instructions on length allowed for other members
     * @return string
     */
    function updateProfile($fields = array()) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $postdata = array();
        foreach ($fields as $pk => $pv) :
            switch ($pk) {
                case 'name' :
                    $postdata[$pk] = (string) substr($pv, 0, 20);
                    break;
                case 'email' :
                    if (preg_match('/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i', $pv))
                        $postdata[$pk] = (string) $pv;
                    break;
                case 'url' :
                    $postdata[$pk] = (string) substr($pv, 0, 100);
                    break;
                case 'location' :
                    $postdata[$pk] = (string) substr($pv, 0, 30);
                    break;
                case 'description' :
                    $postdata[$pk] = (string) substr($pv, 0, 160);
                    break;
                default :
                    break;
            }
        endforeach;

        $request = 'http://twitter.com/account/update_profile.' . $this->type;
        return $this->objectify($this->process($request, $postdata));
    }

    /**
     * Pass an array of values to Twitter to update Twitter profile colors
     * @param array Required. All array members are optional. Optional color fields are: profile_background_color, profile_text_color, profile_link_color, profile_sidebar_fill_color, profile_sidebar_border_color
     * @return string
     */
    function updateColors($colors = array()) {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $postdata = array();
        foreach ($colors as $ck => $cv) :
            if (preg_match('/^(?:(?:[a-f\d]{3}){1,2})$/i', $hex)) :
                $postdata[$ck] = (string) $cv;
            endif;
        endforeach;

        $request = 'http://twitter.com/account/update_profile_colors.' . $this->type;
        return $this->objectify($this->process($request, $postdata));
    }

    /**
     * Rate Limit API Call. Sometimes Twitter needs to degrade. Use this non-ratelimited API call to work your logic out
     * @return integer|boolean 
     */
    function ratelimit() {
        if (!in_array($this->type, array('xml', 'json')))
            return false;
        $request = 'http://twitter.com/account/rate_limit_status.' . $this->type;
        return $this->objectify($out);
    }

    /**
     * Rate Limit statuses (extended). Provides helper data like remaining-hits, hourly limit, reset time and reset time in seconds
     * @deprecated
     */
    function ratelimit_status() {
        return $this->ratelimit();
    }

    /*     * **** Tests ***** */

    /**
     * Detects if Twitter is up or down. Chances are, it will be down. ;-) Here's a hint - display CPM ads whenever Twitter is down
     * @return boolean
     */
    function twitterAvailable() {
        if (!in_array($this->type, array('xml', 'json')))
            return false;

        $request = 'http://twitter.com/help/test.' . $this->type;
        if ($this->objectify($this->process($request)) == 'ok')
            return true;

        return false;
    }

    /**
     * Any prescheduled maintenance?
     * @deprecated
     * @return false
     */
    function maintenanceSchedule() {
        return false;
    }

    /*     * **** Private and Helpers Methods ***** */

    /**
     * Uses the http://is.gd API to produce a shortened URL. Pluggable by extending the twitter class
     * @param string $url The URL needing to be shortened
     * @return string
     */
    function shorturl($url) {
        // Using is.gd because it's good
        $request = 'http://is.gd/api.php?longurl=' . $url;
        return $this->process($request);
    }

    /**
     * Internal function where all the juicy curl fun takes place
     * this should not be called by anything external unless you are
     * doing something else completely then knock youself out.
     * @access private
     * @param string $url Required. API URL to request
     * @param string $postargs Optional. Urlencoded query string to append to the $url
     */
    function process($url, $postargs = false) {
        $url = ( $this->suppress_response_code ) ? $url . '&suppress_response_code=true' : $url;
        $ch = curl_init($url);
        if ($postargs !== false) {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postargs);
        }

        if ($this->username !== false && $this->password !== false)
            curl_setopt($ch, CURLOPT_USERPWD, $this->username . ':' . $this->password);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_VERBOSE, 1);
        curl_setopt($ch, CURLOPT_NOBODY, 0);
        if ($this->debug) :
            curl_setopt($ch, CURLOPT_HEADER, true);
        else :
            curl_setopt($ch, CURLOPT_HEADER, false);
        endif;
        curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent);
        @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers);

        $response = curl_exec($ch);

        $this->responseInfo = curl_getinfo($ch);
        curl_close($ch);


        if ($this->debug) :
            $debug = preg_split("#\n\s*\n|\r\n\s*\r\n#m", $response);
            echo'<pre>' . $debug[0] . '</pre>';
            exit;
        endif;

        if (intval($this->responseInfo['http_code']) == 200)
            return $response;
        else
            return false;
    }

    /*     * *DEPRECATED** */

    /**
     * Returns a list of the users currently featured on the site with their current statuses inline.
     * @deprecated
     * @return string
     */
    function featured() {
        return false;
    }

    /**
     * Updates Geo location
     * @deprecated
     * @param string $location Required. 
     * @return string
     */
    function updateLocation($location) {
        return $this->updateProfile(array('location' => $location));
    }

    /**
     * Send an authenticated request to Twitter for the timeline of authenticating users friends. 
     * Returns the last 20 updates by default
     * @deprecated true
     * @param boolean|integer $id Specifies the ID or screen name of the user for whom to return the friends_timeline. (set to false if you want to use authenticated user).
     * @param boolean|integer $since Narrows the returned results to just those statuses created after the specified date.
     * @return string
     */
    function friendsTimeline($id = false, $since = false, $since_id = false, $count = 20, $page = false) {
        return $this->userTimeline($id, $count, $since, $since_id, $page);
    }

    /**
     * Function to prepare data for return to client
     * @access private
     * @param string $data
     */
    function objectify($data) {
        if ($this->type == 'json')
            return json_decode($data);

        else if ($this->type == 'xml') {
            if (function_exists('simplexml_load_string')) :
                $obj = simplexml_load_string($data);
            endif;
            return $obj;
        }
        else
            return false;
    }

    /**
     * Function to piece together a cohesive query string
     * @access private
     * @param array $array
     * @return string
     */
    function _glue($array) {
        $query_string = '';
        foreach ($array as $key => $val) :
            $query_string .= $key . '=' . rawurlencode($val) . '&';
        endforeach;

        return '?' . substr($query_string, 0, strlen($query_string) - 1);
    }

}

/**
 * Wrapper class around the Twitter Search (formerly, and affectionately known as Summize)
 * @author Aaron Brazell <aaron@technosailor.com>
 * @author Keith Casey <caseysoftware@gmail.com>
 * @version 1.1
 * @package php-twitter
 * @subpackage classes
 */
class summize extends twitter {

    /**
     * Can be set to JSON (requires PHP 5.2 or the json pecl module) or Atom - json|atom
     * @var string
     */
    var $stype = 'json';

    function search($terms = false, $callback = false) {
        if (!$terms)
            return false;

        $qs = array();
        $request = 'http://search.twitter.com/search.' . $this->stype;

        $qs[] = 'q=' . rawurlencode($terms);
        if ($callback && $this->stype == 'json')
            $qs[] = 'callback=' . $callback;

        return $this->objectify($this->process($request . '?' . implode('&', $qs)));
    }

    function trends($callback = false) {
        $qs = ( $callback ) ? '?callback=' . $callback : '';
        $request = 'http://search.twitter.com/trends.' . $this->stype . $qs;

        return $this->objectify($this->process($request));
    }

}

?>